﻿using System;

namespace RGBGame
{
    public class GameLogic
    {
        public const int CELL_NO_MATCH = -1000;
        internal int myTurn = 0, opTurn = 0;
        internal int myScore = 0, opScore = 0;
        internal int[] AIRandomCells = null;
        protected GameTools rgbSub=null;
        internal Colors[][] tileStatus = new Colors[6][];

        internal Colors MyColor
        {
            get
            {
                return (Colors)((myTurn % 3) + 1);
            }
        }

        internal Colors OpColor
        {
            get
            {
                return (Colors)((opTurn % 3) + 1);
            }
        }

        internal Colors getColorByi(int i)
        {
            if (i < 0 || i > 35)
                return Colors.OutOfRange;

            TilePoint pos = TilePoint.get(i);
            return tileStatus[pos.X][pos.Y];
        }

        internal bool checkFit(int i, int diff1, int diff2, Colors clr)
        {
            int yi = TilePoint.getY(i);
            int y1 = TilePoint.getY(i+diff1);
            int y2 = TilePoint.getY(i+diff2);

            if (diff1 > diff2 != y1 > y2)
            {
                return false;
            }

            if (yi == y1 || yi == y2 || y1==y2)
            {
                return false;
            }

            if (Math.Abs(y1 - yi) > 1)
                return false;

            bool upper = (y2 - yi) == 1 || (y2-yi==2 && y1-yi==1);
            bool downer = (yi - y2) == 2;

            if(!upper && !downer)
                return false;

            bool score = getColorByi(i + diff1) == clr && getColorByi(i + diff2) == clr;
            
            return score;
        }

        internal bool checkFitX(int i, int diff1, int diff2, Colors clr)
        {
            if ((i % 6) + diff1 < 0 || (i % 6) + diff1 > 5)
                return false;

            if ((i % 6) + diff2 < 0 || (i % 6) + diff2 > 5)
                return false;

            return getColorByi(i + diff1) == clr && getColorByi(i + diff2) == clr;
        }

        internal int checkScore(int i, Colors clr)
        {
            for (int n = -7; n < -4; n++)
                if (checkFit(i, n, 2 * n, clr))
                    return 2 * n;

            for (int n = -7; n < -4; n++)
                if (checkFit(i, n, -1 * n, clr))
                    return n;

            for (int n = 5; n < 8; n++)
                if (checkFit(i, n, 2 * n, clr))
                    return 2 * n;

            if (checkFitX(i, -2, -1, clr))
                return -2;

            if (checkFitX(i, -1, 1, clr))
                return -1;

            if (checkFitX(i, 1, 2, clr))
                return 2;

            return CELL_NO_MATCH;
        }

        internal static int [] scoreCells(int i, int result)
        {
            int a, b;
            if (result < -7 || result > 7 || result == -2 || result == 2)
            {
                a = i + (result / 2);
                b = i + (result);
            }
            else
            {
                a = i + (result);
                b = i - (result);
            }

            return new int[]{i,a,b};
        }

        internal Colors getColorByXY(int x, int y)
        {
            if(x>=0 && y>=0 && x<6 && y<6)
            {
                return tileStatus[x][y];
            }

            return Colors.OutOfRange;
        }

        private static readonly int[] comparisons = new int[]{-1, 1};
        internal bool matchAround(int i, Colors cr)
        {
            TilePoint pos = TilePoint.get(i);
            for(int x=0;x<2;x++)
            {
                for (int y = 0; y < 2; y++)
                {
                    Colors c = getColorByXY(pos.X + comparisons[x], pos.Y + comparisons[y]);
                    if(c!=Colors.OutOfRange)
                    {
                        if (c == cr)
                            return true;
                    }
                }    
            }
            return false;
        }

        private int suggestEmptyPlace(Colors cr)
        {
            int n = 36;
            while (n-- > 0)
            {
                int i = AIRandomCells[n];
                TilePoint xy = TilePoint.get(i);
                Colors ch = tileStatus[xy.X][xy.Y];
                if (ch == Colors.Transparent)
                {
                    if (!matchAround(i, cr))
                        return i;
                }
            }

            return CELL_NO_MATCH;
        }

        /// <summary>
        /// finds a cell where one can get a score
        /// </summary>
        /// <returns></returns>
        internal int findGameMatch(Colors clr)
        {
            int n=36;
            while(n-->0)
            {
                int i = AIRandomCells[n];

                TilePoint xy = TilePoint.get(i);
                Colors ch = tileStatus[xy.X][xy.Y];
                if (ch == Colors.Transparent)
                {
                    int pos = checkScore(i, clr);
                    if (pos != CELL_NO_MATCH)
                        return i;
                }
            }

            return CELL_NO_MATCH;
        }

        internal void playTurn(Colors c)
        {
            int opIndex = findGameMatch(OpColor);
            int myIndex = findGameMatch(MyColor);

            if (opIndex == CELL_NO_MATCH && myIndex != CELL_NO_MATCH)
            {
                playTurn(myIndex);
                return;
            }

            if (opIndex != CELL_NO_MATCH && myIndex == CELL_NO_MATCH)
            {
                playTurn(opIndex);
                return;
            }

            if (opIndex != CELL_NO_MATCH && myIndex != CELL_NO_MATCH)
            {
                if(myScore<=opScore)
                    playTurn(myIndex);
                else
                {
                    playTurn(opIndex);
                }
                return;
            }

            if (opIndex == CELL_NO_MATCH && myIndex == CELL_NO_MATCH)
            {
                myIndex = suggestEmptyPlace(MyColor);
                if(myIndex != CELL_NO_MATCH)
                {
                    playTurn(myIndex);
                    return;
                }

                opIndex = suggestEmptyPlace(OpColor);
                if (opIndex != CELL_NO_MATCH)
                {
                    playTurn(opIndex);
                    return;
                }

                int n = 36;
                while (n-- > 0)
                {
                    int i = AIRandomCells[n];

                    TilePoint xy = TilePoint.get(i);
                    Colors ch = tileStatus[xy.X][xy.Y];
                    if (ch == Colors.Transparent)
                    {
                        playTurn(i);
                        return;
                    }
                }
            }
        }

        private void playTurn(int index)
        {
            rgbSub.playTile(index);
        }

        internal void markTile(int i, Colors ch)
        {
            rgbSub.cellsFilled++;
            TilePoint xy = TilePoint.get(i);

            //check if painted by score event already
            if (tileStatus[xy.X][xy.Y] == Colors.Transparent)
            {
                tileStatus[xy.X][xy.Y] = ch;

                if (!rgbSub.AIPlayer)
                {
                    rgbSub.tiles[i].BackColor = Utility.getColor(ch);
                }
            }

            rgbSub.gameEndCheck();
        }
    }
}